<template>
  <div class="stream-player">
    <div class="stream-header">
      <input
        v-model="rtspUrl"
        type="text"
        placeholder="rtsp://用户名:密码@IP:端口/Streaming/Channels/102"
        class="url-input"
        :disabled="isPlaying"
      />
      <button @click="handleRemove" class="remove-btn" title="删除画面">×</button>
    </div>

    <div class="stream-controls">
      <button @click="handlePlay" :disabled="isPlaying || !rtspUrl.trim()" class="play-btn">
        {{ isPlaying ? '播放中...' : '播放' }}
      </button>
      <button @click="handleStop" :disabled="!isPlaying" class="stop-btn">
        停止
      </button>
      <span v-if="error" class="error-msg">{{ error }}</span>
    </div>

    <div class="canvas-container">
      <canvas :ref="setCanvasRef" class="video-canvas"></canvas>
      <div v-if="!isPlaying" class="placeholder">
        <span class="placeholder-text">{{ error || '等待播放...' }}</span>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, onUnmounted, onMounted } from 'vue'

const props = defineProps({
  streamId: {
    type: Number,
    required: true
  },
  initialUrl: {
    type: String,
    default: ''
  }
})

const emit = defineEmits(['remove'])

const KEEP_ALIVE_INTERVAL = 58000 // 58秒

const rtspUrl = ref(props.initialUrl)
const isPlaying = ref(false)
const error = ref('')
const canvasElement = ref(null)
const player = ref(null)
const keepAliveTimer = ref(null)

// 加载 JSMpeg 脚本
const loadJSMpeg = () => {
  return new Promise((resolve, reject) => {
    if (window.JSMpeg) {
      resolve()
      return
    }

    const script = document.createElement('script')
    script.src = '/jsmpeg.min.js'
    script.onload = () => resolve()
    script.onerror = () => reject(new Error('加载 JSMpeg 失败'))
    document.head.appendChild(script)
  })
}

// 设置 canvas 引用
const setCanvasRef = (el) => {
  canvasElement.value = el
}

// 请求播放流
const requestStream = async (url) => {
  try {
    const response = await fetch('/stream/play', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ url })
    })

    if (!response.ok) {
      throw new Error('网络请求失败')
    }

    const data = await response.json()

    if (data.code !== 0 || !data.data || !data.data.path) {
      throw new Error(data.msg || '接口返回异常')
    }

    return data.data.path
  } catch (err) {
    throw new Error(err.message || '请求失败')
  }
}

// 构建 WebSocket URL
const buildWsUrl = (path) => {
  const protocol = window.location.protocol === 'https:' ? 'wss://' : 'ws://'
  return `${protocol}${window.location.hostname}:3000${path}`
}

// 播放
const handlePlay = async () => {
  const url = rtspUrl.value.trim()
  if (!url) {
    error.value = '请输入有效的 RTSP 地址'
    return
  }

  error.value = ''
  isPlaying.value = true

  try {
    // 确保 JSMpeg 已加载
    await loadJSMpeg()

    // 请求播放流
    const path = await requestStream(url)
    const wsUrl = buildWsUrl(path)

    // 销毁旧的播放器
    if (player.value) {
      player.value.destroy()
      player.value = null
    }

    // 创建新的播放器
    if (window.JSMpeg && canvasElement.value) {
      player.value = new window.JSMpeg.Player(wsUrl, {
        canvas: canvasElement.value,
        disableGl: false,
        preserveDrawingBuffer: false
      })
    } else {
      throw new Error('JSMpeg 未正确加载或 Canvas 不可用')
    }

    // 启动保活定时器
    startKeepAlive(url)

  } catch (err) {
    error.value = `播放失败：${err.message}`
    isPlaying.value = false
    console.error('播放错误:', err)
  }
}

// 停止播放
const handleStop = () => {
  stopKeepAlive()

  if (player.value) {
    player.value.destroy()
    player.value = null
  }

  isPlaying.value = false
  error.value = ''
}

// 启动保活定时器
const startKeepAlive = (url) => {
  stopKeepAlive()

  keepAliveTimer.value = setInterval(async () => {
    try {
      await requestStream(url)
    } catch (err) {
      console.warn('保持拉流失败：', err.message)
    }
  }, KEEP_ALIVE_INTERVAL)
}

// 停止保活定时器
const stopKeepAlive = () => {
  if (keepAliveTimer.value) {
    clearInterval(keepAliveTimer.value)
    keepAliveTimer.value = null
  }
}

// 删除画面
const handleRemove = () => {
  handleStop()
  emit('remove', props.streamId)
}

// 组件挂载
onMounted(() => {
  loadJSMpeg().catch(err => {
    error.value = err.message
  })
})

// 组件卸载
onUnmounted(() => {
  handleStop()
})
</script>

<style scoped>
.stream-player {
  background-color: #fff;
  border-radius: 8px;
  padding: 16px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.stream-header {
  display: flex;
  gap: 8px;
  align-items: center;
}

.url-input {
  flex: 1;
  font-size: 14px;
}

.url-input:disabled {
  background-color: #f5f5f5;
  cursor: not-allowed;
}

.remove-btn {
  width: 36px;
  height: 36px;
  padding: 0;
  font-size: 24px;
  line-height: 1;
  background-color: #f44336;
  border-radius: 4px;
  flex-shrink: 0;
}

.remove-btn:hover {
  background-color: #d32f2f;
}

.stream-controls {
  display: flex;
  gap: 8px;
  align-items: center;
}

.play-btn {
  background-color: #4caf50;
}

.play-btn:hover:not(:disabled) {
  background-color: #45a049;
}

.play-btn:disabled {
  background-color: #81c784;
}

.stop-btn {
  background-color: #ff9800;
}

.stop-btn:hover:not(:disabled) {
  background-color: #fb8c00;
}

.error-msg {
  color: #f44336;
  font-size: 12px;
  margin-left: auto;
}

.canvas-container {
  position: relative;
  width: 100%;
  background-color: #000;
  border-radius: 4px;
  overflow: hidden;
  min-height: 300px;
}

.video-canvas {
  width: 100%;
  height: auto;
  display: block;
}

.placeholder {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: rgba(0, 0, 0, 0.8);
}

.placeholder-text {
  color: #fff;
  font-size: 14px;
}
</style>

